package request

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"fmt"
	request "github.com/parnurzeal/gorequest"
	"github.com/wailsapp/wails/v2/pkg/runtime"
	"io"
	"net/http"
	"sync/atomic"
)

type Request struct {
	Debug   bool
	request *request.SuperAgent
	//request []*request.SuperAgent
	//curPool *int32
	//poolSize int32
}

type Certificate struct {
	RootCA    string
	ClientCrt string
	ClientKey string
}

type Config struct {
	Url         string
	Header      map[string]string
	Params      map[string]interface{}
	Data        interface{}
	Certificate *Certificate
	Timeout     int64
	Proxy       bool
	Channel     string
	Method      string
}

type Response struct {
	Header  http.Header
	Status  int
	Error   string
	RawData []byte
	Data    interface{}
}

type Pool struct {
	poolSize int32
	request  []*Request
	cur      *int32
}

func NewPool(poolSize int32) *Pool {
	pool := new(Pool)
	pool.cur = new(int32)
	pool.poolSize = poolSize
	pool.request = make([]*Request, 0, poolSize)
	for i := int32(0); i < poolSize; i++ {
		pool.request = append(pool.request, NewRequest())
	}

	return pool
}

func (p *Pool) GetRequest() *Request {
	req := p.request[atomic.AddInt32(p.cur, 1)]
	if atomic.LoadInt32(p.cur) >= p.poolSize-1 {
		atomic.StoreInt32(p.cur, -1)
	}
	return req
}

func NewRequest() *Request {
	r := new(Request)
	r.request = request.New()
	return r
}

func (r *Request) Request(config *Config) (resp request.Response, data string, err error) {
	var req *request.SuperAgent

	if r.request == nil {
		req = request.New()
	} else {
		req = r.request
	}

	req.CustomMethod(config.Method, config.Url)

	if config.Certificate != nil {
		caCertPool := x509.NewCertPool()
		caCert, decodeErr := base64.StdEncoding.DecodeString(config.Certificate.RootCA)
		if decodeErr != nil {
			fmt.Println("ca decodeError")
			err = decodeErr
			return
		}
		caCertPool.AppendCertsFromPEM(caCert)

		cliCertFile, decodeErr := base64.StdEncoding.DecodeString(config.Certificate.ClientCrt)
		if decodeErr != nil {
			fmt.Println("cliCert decodeError")
			err = decodeErr
			return
		}

		cliCertKey, decodeErr := base64.StdEncoding.DecodeString(config.Certificate.ClientKey)
		if decodeErr != nil {
			fmt.Println("cliKey decodeError")
			err = decodeErr
			return
		}

		cliCert, decodeErr := tls.X509KeyPair(cliCertFile, cliCertKey)
		if decodeErr != nil {
			err = decodeErr
			return
		}

		req.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs:      caCertPool,
				Certificates: []tls.Certificate{cliCert},
			},
		}
	} else {
		req.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	if config.Header != nil {
		for key, value := range config.Header {
			req.Header.Add(key, value)
		}
	}

	if config.Method == request.POST {
		switch data := config.Data.(type) {
		case map[string]interface{}:
			req.Data = data
		}
	}

	resp, body, errs := req.End()
	if len(errs) != 0 {
		err = errs[len(errs)-1]
		req.Errors = errs[:len(errs)-1]
		return
	}

	data = body
	return
}

func EventsHandler(ctx context.Context, optionalData ...interface{}) {
	configJson := optionalData[0].(string)
	config := new(Config)
	err := json.Unmarshal([]byte(configJson), config)

	defer func() {
		err := recover()
		if err != nil {
			runtime.LogDebugf(ctx, "error: %s", err.(error).Error())
			runtime.EventsEmit(ctx, config.Channel, &Response{
				Status: 500,
				Error:  err.(error).Error(),
				Data:   nil,
			})
		}
	}()

	if err != nil {
		panic(err)
	}

	requestHandler := new(Request)
	resp, data, err := requestHandler.Request(config)
	if err != nil {
		panic(err)
	}

	rawData, _ := io.ReadAll(resp.Body)

	runtime.EventsEmit(ctx, config.Channel, &Response{
		Header:  resp.Header,
		Status:  resp.StatusCode,
		Error:   "",
		RawData: rawData,
		Data:    data,
	})
}
